﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.Entities.Providers
{
    [Serializable]
    public class PartitionInstruction:InstructionBase
    {
        #region props
        private DataProviderType _DbProvider;
        /// <summary></summary>
        public DataProviderType DbProvider
        {
            get
            {
                return this._DbProvider;
            }
            set
            {
                this._DbProvider = value;
            }
        }

        private string _ConnStr;
        /// <summary></summary>
        public string ConnStr
        {
            get
            {
                return this._ConnStr;
            }
            set
            {
                this._ConnStr = value;
            }
        }

        private string _SelectSql;
        /// <summary></summary>
        public string SelectSql
        {
            get
            {
                return this._SelectSql;
            }
            set
            {
                this._SelectSql = value;
            }
        }
        
        private string _IdAttributeName;
        /// <summary></summary>
        public string IdAttributeName
        {
            get
            {
                return this._IdAttributeName;
            }
            set
            {
                this._IdAttributeName = value;
            }
        }

        private string _EntityCategory;
        /// <summary></summary>
        public string EntityCategory
        {
            get
            {
                return this._EntityCategory;
            }
            set
            {
                this._EntityCategory = value;
            }
        }

        private List<AttributePartition> _AttrPartitions;
        /// <summary></summary>
        public List<AttributePartition> AttrPartitions
        {
            get
            {
                return this._AttrPartitions;
            }
            set
            {
                this._AttrPartitions = value;
            }
        }

        private int _MinCountPerPartition;
        /// <summary></summary>
        public int MinCountPerPartition
        {
            get
            {
                return this._MinCountPerPartition;
            }
            set
            {
                this._MinCountPerPartition = value;
            }
        }
       
        private string _PartitionResultFilePath;
        /// <summary>
        /// <Partitions AttributeName="" BinSize="">
        ///     <Partition PartitionID="1" AttributeValueFrom="" AttributeValueTo="">
        ///         <Entity EntityID="1"/>
        ///     </Partition>
        /// </Partitions>
        /// </summary>
        public string PartitionResultFilePath
        {
            get
            {
                return this._PartitionResultFilePath;
            }
            set
            {
                this._PartitionResultFilePath = value;
            }
        }
        #endregion

        #region ctor
        public PartitionInstruction():base(Guid.Empty,Guid.Empty)
        {
            this._DbProvider = DataProviderType.MSSQL;
            this._ConnStr = string.Empty;
            this._SelectSql = string.Empty;
            this._IdAttributeName = string.Empty;
            this._EntityCategory = string.Empty;
            this._AttrPartitions = new List<AttributePartition>();
            this._MinCountPerPartition = 100;
            this._PartitionResultFilePath = string.Empty;
        }

        public PartitionInstruction(Guid componentID, Guid jobTicketID,
            DataProviderType dbProvider,
            string connStr,
            string selectSql,
            string idAttrName, string entityCategory,
            List<AttributePartition> attrPartitions,
            int minCountPerPartition,
            string resultFilePath) 
            : base(componentID, jobTicketID)
        {
            this._DbProvider = dbProvider;
            this._ConnStr = connStr;
            this._SelectSql = selectSql;
            this._IdAttributeName = idAttrName;
            this._EntityCategory = entityCategory;
            this._AttrPartitions = attrPartitions;
            this._MinCountPerPartition = minCountPerPartition;
            this._PartitionResultFilePath = resultFilePath;
        }
        #endregion

        public override InstructionBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                PartitionInstruction instruction = new PartitionInstruction();
                instruction.OwnerComponentID = new Guid(xNode.Attributes.GetNamedItem("ComponentID").Value);
                instruction.OwnerWorkerInstanceID = new Guid(xNode.Attributes.GetNamedItem("WorkerID").Value);
                instruction.SubmitTime = DateTime.Parse(xNode.Attributes.GetNamedItem("SubmitTime").Value);
                instruction.StartTime = DateTime.Parse(xNode.Attributes.GetNamedItem("StartTime").Value);

                // 
                instruction.IdAttributeName = XmlDataUtil.GetAttributeValue(xNode, "IdAttributeName", "");
                instruction.EntityCategory = XmlDataUtil.GetAttributeValue(xNode, "EntityCategory", "");

                // provider
                this._DbProvider =
                    (DataProviderType)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "ProviderDbProvider",
                                                            ((int) DataProviderType.File).ToString()));
                this._ConnStr = XmlDataUtil.GetAttributeValue(xNode, "ProviderConnStr", "");
                this._SelectSql = XmlDataUtil.GetAttributeValue(xNode, "ProviderSelectSql", "");

                // partition 
                XmlNodeList attrPartitionNodes = xNode.SelectNodes("Partitions/Partition");
                if (attrPartitionNodes != null && attrPartitionNodes.Count > 0)
                {
                    instruction.AttrPartitions = new List<AttributePartition>();
                    foreach (XmlNode attrPartitionNode in attrPartitionNodes)
                    {
                        AttributePartition attrPartition = new AttributePartition();
                        attrPartition.AttributeName = XmlDataUtil.GetAttributeValue(
                            attrPartitionNode, "PartitionAttributeName", "");
                        attrPartition.Method = (PartitionMethod) int.Parse(
                                                                     XmlDataUtil.GetAttributeValue(
                                                                         attrPartitionNode, "PartitionMethod",
                                                                         ((int) PartitionMethod.Percent).ToString()));
                        attrPartition.BinSize =
                            double.Parse(XmlDataUtil.GetAttributeValue(
                                             attrPartitionNode, "PartitionBinSize", "5"));
                        instruction.AttrPartitions.Add(attrPartition);
                    }
                }

                instruction.MinCountPerPartition =
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "MinCountPerPartition", "100"));

                // result 
                instruction.PartitionResultFilePath = XmlDataUtil.GetAttributeValue(xNode, "ResultFilePath", "");

                return instruction;
            }
            else
            {
                throw new Exception("Unable to instantiate instruction: invalid type specified in xml node");
            }
        }

        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Instruction");
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComponentID", this.OwnerComponentID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SubmitTime", this.SubmitTime.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "StartTime", this.StartTime.ToString());

            // meta data
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "IdAttributeName", this.IdAttributeName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "EntityCategory", this.EntityCategory);

            // provider 
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ProviderDbProvider", ((int)this.DbProvider).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ProviderConnStr", this.ConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ProviderSelectSql", this.SelectSql);

            // partition 
            XmlNode partitionRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Partitions");
            if(this.AttrPartitions !=null && this.AttrPartitions.Count>0)
            {
                foreach(AttributePartition attrPartition in this.AttrPartitions)
                {
                    XmlNode partitionNode = XmlDataUtil.AddElement(ref xDoc, partitionRootNode, "Partition");
                    XmlDataUtil.UpdateAttribute(
                        ref xDoc, partitionNode, "PartitionAttributeName",
                        attrPartition.AttributeName);
                    XmlDataUtil.UpdateAttribute(
                        ref xDoc, partitionNode, "PartitionMethod", 
                        ((int)attrPartition.Method).ToString());
                    XmlDataUtil.UpdateAttribute(
                        ref xDoc, partitionNode, "PartitionBinSize", 
                        attrPartition.BinSize.ToString());

                }
            }

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "MinCountPerPartition", this.MinCountPerPartition.ToString());
            
            // result
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ResultFilePath", this.PartitionResultFilePath);
            return xNode;
        }
    }
}
